#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-

# @Time    :2023/05/02 09:55:51
# @Author  :wakeblade (2390245#qq.com) 
# @version :8.1

"""
proxy.py -- WtQuant的API代理, 主要处理api访问权限问题
"""

import six
import requests
from functools import wraps
import json
from typing import Any
from enum import Enum
import pandas as pd
from threading import Thread
import io
import time

def fixHeader(api:Any, headers:dict, needToken: bool = True):
    # 如果需要token，需要在headers带上token
    if needToken:
        headers["Authorization"] = f"Bear {api.token}"
    elif "Authorization" in headers:
        headers.pop("Authorization")
    return headers

def fixCmd(func:callable, kwargs:dict, paramsInPath:list = None):
    # 如果参数包含了cmd,则取cmd，否则默认取函数名
    cmd = kwargs.pop("cmd") if "cmd" in kwargs else func.__name__
    if not paramsInPath:
        return cmd
    
    for param in paramsInPath:
        cmd = f"{cmd}/{kwargs.pop(param)}"
    
    return cmd

ResType = Enum("ResType","TEXT EVAL JSON BYTES DATAFRAME".split())

def resAsType(res, encoding:str , resType:ResType):
    # print("########## resAsType", res.text)
    assert isinstance(resType, ResType), "参数 resType 必须是 ResType 类型！"

    res.encoding = encoding

    if resType is ResType.TEXT:
        return res.text

    if resType is ResType.EVAL:
        return eval(res.text)

    if resType is ResType.JSON:
        return json.loads(res.text)

    if resType is ResType.BYTES:
        return res.content
    
    if res.content[0] in b"{[":
        return json.loads(res.text)

    return pd.read_parquet(io.BytesIO(res.content))

def _get(needToken: bool = True, encoding:str = "utf8", resType:ResType = ResType.JSON, paramsInPath:list = None):
    headers = {'User-Agent': 'Mozilla/5.0'} 
    def docorator(func:callable):
        @wraps(func)
        def decorated(api:Any, **kwargs):
            # 如果需要token，需要在headers带上token
            fixHeader(api, headers, needToken)

            # 如果参数包含了cmd,则取cmd，否则默认取函数名
            cmd = fixCmd(func, kwargs, paramsInPath)

            # 调用request.get方法，取出response中返回结果
            res = requests.get(url=f"{api.uri}/{cmd}", headers=headers, params=kwargs)
            return resAsType(res, encoding, resType) 
        return decorated
    return docorator
 
def _post(needToken: bool = True, encoding="utf8", resType:ResType = ResType.JSON, paramsInPath:list = None):
    headers = {'User-Agent': 'Mozilla/5.0'} 
    def docorator(func:callable):
        @wraps(func)
        def decorated(api:Any, **kwargs):
            # 如果需要token，需要在headers带上token
            fixHeader(api, headers, needToken)

            # 如果参数包含了cmd,则取cmd，否则默认取函数名
            cmd = fixCmd(func, kwargs, paramsInPath)

            # 调用request.post方法，取出response中返回结果
            res = requests.post(url=f"{api.uri}/{cmd}", headers=headers, data=json.dumps(kwargs))
            return resAsType(res, encoding, resType) 
        return decorated
    return docorator
 
def _put(needToken: bool = True, encoding="utf8", resType:ResType = ResType.JSON, paramsInPath:list = None):
    headers = {'User-Agent': 'Mozilla/5.0'} 
    def docorator(func:callable):
        @wraps(func)
        def decorated(api:Any, **kwargs):
            # 如果需要token，需要在headers带上token
            fixHeader(api, headers, needToken)

            # 如果参数包含了cmd,则取cmd，否则默认取函数名
            cmd = fixCmd(func, kwargs, paramsInPath)

            # 调用request.post方法，取出response中返回结果
            res = requests.put(url=f"{api.uri}/{cmd}", headers=headers, data=json.dumps(kwargs))
            return resAsType(res, encoding, resType) 
        return decorated
    return docorator
 
def _delete(needToken: bool = True, encoding="utf8", resType:ResType = ResType.JSON, paramsInPath:list = None):
    headers = {'User-Agent': 'Mozilla/5.0'} 
    def docorator(func:callable):
        @wraps(func)
        def decorated(api:Any, **kwargs):
            # 如果需要token，需要在headers带上token
            fixHeader(api, headers, needToken)

            # 如果参数包含了cmd,则取cmd，否则默认取函数名
            cmd = fixCmd(func, kwargs, paramsInPath)

            # 调用request.get方法，取出response中返回结果
            res = requests.delete(url=f"{api.uri}/{cmd}", headers=headers, params=kwargs)
            return resAsType(res, encoding, resType) 
        return decorated
    return docorator
     
@_post(needToken=False)
def login(api:Any, appKey:str, appSecret:str):
    pass

@_get()
def heartbeat(api:Any, appKey:str):
    pass

@_get()
def logout(api:Any):
    pass

class ApiProxy:
    """ ApiProxy对象

    Arguments:
        appKey (str): 
            开发者key.
        appSecret (str): 
            开发者密钥.
        uri (str): 
            api服务器的uri.
        token (str): 
            开发者登陆后的访问令牌.
    """

    # 初始化配置key和secret和uri
    def __init__(self, appKey:str, appSecret:str, uri:str) -> None:
        self.appKey = appKey
        self.appSecret = appSecret
        self.uri = uri
        self.token = ""

    # 登录获取token
    def login(self):
        res = login(self, appKey=self.appKey, appSecret=self.appSecret)
        # print(res)
        self.token = res["detail"] if res["status_code"] == 200 else ""
        self.heart_active = True
        if six.PY2:
            self.heart_thread = Thread(target = self.heartbeat)
            self.heart_thread.setDaemon(True)
        else:
            self.heart_thread = Thread(target = self.heartbeat, daemon=True)
        self.heart_thread.start()
    
    def heartbeat(self):
        while True:
            try:
                res = heartbeat(self, appKey=self.appKey)
                print("############# heartbeat", res)
                if res.get("status_code", -1) == 200 and "detail" in res:
                    self.token = res["detail"]

                time.sleep(10)
            except Exception as e:
                print(e)
                time.sleep(10)

    # 登录获取token
    def logout(self):
        self.heart_active = False
        res = logout(self)
        print(res)
        self.token = ""

if __name__ == "__main__":
    appKey = "wakeblade"
    appSecret = "Ay7WH7Vk4aU1I7Y4AQte9beBlQqLWzes"
    print(f"appKey: {appKey} | appSecret: {appSecret}")
    api = ApiProxy(appKey, appSecret, "http://127.0.0.1/api")
    api.login()
    while True:
        print(api.__dict__)
        time.sleep(10)
    res = api.logout()
    print(api.__dict__)
